import numpy
import random
import copy

class Hiker:
	mapp = 0
	alive = True
	location = [0,0]
	experience_level = 0 #   0 for a novice hiker, 1 for an experienced hiker
	physical_state = 0 #   0 for healthy, 1 for slightly injured, 2 for severely injured
	max_steps = 20
	max_water = 5
	current_steps = 0
	current_water = max_water
	mental_state = 0 # 0 for sane, 1 for slightly impaired, 2 for panicked
	max_sense_of_direction = 0.50 # This is the maximum probability that a hiker knows where his base is relative to himself, without seeing it.
	current_sense_of_direction = max_sense_of_direction
	badluck = .002 #The minimum chance that the hiker is going to hurt himself during a move.

	def __init__(self, the_map, stats):
		self.mapp = the_map
		self.alive = True
		self.location = [0,0]
		self.experience_level = stats[0]
		self.physical_state = 0
		self.max_steps = stats[1]
		self.max_water = stats[2]
		self.current_steps = 0
		self.current_water = self.max_water
		self.mental_state = 0
		self.max_sense_of_direction = stats[3]
		self.current_sense_of_direction = self.max_sense_of_direction
		self.badluck = stats[4]


	def go_get_hurt(self, chance):
		if random.random() < chance:
			self.physical_state +=1
			if self.physical_state > 2:
				self.alive = False
				self.physical_state = 2


	#attemps to move the hiker in the specified direction. If he cannot move, his stats won't change.
	def move(self, direction): #direction will be a vector like [1,0] or [0,-1] 
		chance_of_injury = self.badluck
		newloc = [self.location[0] + direction[0], self.location[1] + direction[1]]
		steps_required = 1
		terrain = self.mapp[self.location[0]][self.location[1]]
		newterrain = self.mapp[newloc[0]][newloc[1]]
		if abs(newterrain[0] - terrain[0]) > 1:
			return False
		elif newterrain[0] > terrain[0]:
			if self.physical_state == 2:
				return False
			chance_of_injury*=2
			steps_required = 2 
		elif newterrain[0] < terrain[0]:
			chance_of_injury*=4
		self.go_get_hurt(chance_of_injury)
		self.current_steps += steps_required
		self.location = newloc
		if self.has_water([0,0]):
			self.current_water = self.max_water
			self.mental_state = 0
		return True

	def drink(self):
		if self.current_water > 0:
			self.current_water -=1
		else:
			if self.mental_state < 2:
				self.mental_state+=1
		if self.current_water < -3:
			self.alive = False

	def printStats(self):
		print "Current stats of this hiker:"
		print("Alive" if self.alive else "Dead")
		print "Located at", self.location
		print("Experienced hiker" if self.experience_level == 1 else "Novice hiker")
		p_states = { 0 : "healthy", 1:"slightly injured", 2:"severely injured" }
		print "The hiker is ", p_states[self.physical_state]
		print "The number of steps this hiker can take in a day is", self.max_steps
		print "Steps taken today: ", self.current_steps
		print "Max days supply of water:", self.max_water, ". Days of water left:", self.current_water
		m_states = {0: "sane.", 1:"slightly impaired.", 2:"panicked."}
		print "Mental state is: ", m_states[self.mental_state]
		print "Maximum probability of knowing the direction to base:", self.max_sense_of_direction
		print "Current probability of knowing the direction to base:", self.current_sense_of_direction
		print "Badluck is", self.badluck
	
	#This is a VERY simple line-of-sight algorithm, and is by no means accurate.
	def los(self, loc):
		in_los = True
		relative_position = [loc[0] - self.location[0], loc[1] - self.location[1]]
		factor = gcf(abs(relative_position[0]),abs(relative_position[1]))
		sight_line = [relative_position[0]/factor, relative_position[1]/factor]
		curr_position = copy.copy(self.location)
	
		while(loc != curr_position):
			#if the terrain is greater
			if(self.mapp[curr_position[0], curr_position[1]][0] > self.mapp[self.location[0],self.location[1]][0]):
				in_los = False
			curr_position[0] += sight_line[0]
			curr_position[1] += sight_line[1]
		return in_los

	def has_water(self, direction):
		return self.mapp[self.location[0] + direction[0], self.location[1] + direction[1]][1] == 1

	def p_water(self, priorities):
		if self.experience_level == 1:
			dir_list = [[0,1],[1,0],[0,-1],[-1,0]]
			g = 0
			for i in dir_list:
				if self.has_water(i):
					if self.current_water < 3:
						priorities[g] += 3*(3-current_water)
						g+=1


	def choose_the_way(self):
		#These correspond to the priorities of directions N, E, S, W, respectively. ([0,1], [1,0], [0,-1], [-1,0])
		priorities = [0,0,0,0]
		priorities_dict = { 0 : [0,1], 1 : [1,0], 2 : [0,-1], 3 : [-1,0] }
		if(random.random() < self.current_sense_of_direction):
			#since the base is always at 0,0
			if self.location[0] > 0: #if you're far to the east, prioritize going west
				priorities[3] = 10 * abs(self.location[0])
			else:
				priorities[1] = 10 * abs(self.location[0])
			if self.location[1] > 0:
				priorities[2] = 10 * abs(self.location[1])
			else:
				priorities[0] = 10 * abs(self.location[1])

		self.p_water(priorities)
		
		mental_mistakes = {0:0, 1:.2, 2:.5}
		mistake_chance = mental_mistakes[self.mental_state]
		list_priorities = [ [0, priorities[0]], [1, priorities[1]], [2, priorities[2]], [3, priorities[3]] ]

		def compare(a,b):
			return cmp(b[1], a[1])
		

		list_priorities.sort(compare)
	
		for choice in list_priorities:
			if random.random() < mistake_chance:
				moved = self.move(priorities_dict[random.randint(0,3)])
				if moved:
					return
				else:
					continue
			moved = self.move(priorities_dict[choice[0]])
			if moved:
				return

		self.alive = False


def gcf(a,b):
	while (a!=b) and a > 0 and b > 0:
		if(a>b):
			a=a-b
		else:
			b=b-a
	return a if a!=0 else b
